#include "View.h"
#include "Math.h"

U32 View::GetPix(F64 nRow, F64 nCol) const
{
	TransformPoints(&nRow, &nCol);
	
	F64 nRowDiff = nRow - floor(nRow);
	F64 nColDiff = nCol - floor(nCol);
	
	/* our pixel is in the form of:
	 * p1             p2
	 *
	 * p13    p1234   p24
	 *
	 *
	 * p3             p4
	 */
	F64 p1 = m_pImage->GetPix((U32)nRow + 0, (U32)nCol + 0);
	F64 p2 = m_pImage->GetPix((U32)nRow + 0, (U32)nCol + 1);
	F64 p3 = m_pImage->GetPix((U32)nRow + 1, (U32)nCol + 0);
	F64 p4 = m_pImage->GetPix((U32)nRow + 1, (U32)nCol + 1);
	
	F64 p13 = p1 * (1 - nRowDiff) + p3 * nRowDiff;
	F64 p24 = p2 * (1 - nRowDiff) + p4 * nRowDiff;
	
	F64 p1234 = p13 * (1 - nColDiff) + p24 * nColDiff;
	
	return (U32)p1234;
}

Image *View::ToImage() const {
	Rect<S32> rect;
	S32 nWidth, nHeight, nRight, nBottom;
	GetBoundingRect(&rect);
	nWidth = (nRight = rect.Right()) - rect.Left() + 1;
	nHeight = (nBottom = rect.Bottom()) - rect.Top() + 1;
	Image *pImage = new Image(nWidth, nHeight);
	U8 *pData = pImage->GetData();
	OutputPixels(pData, 0, false);
	return pImage;
}

void View::OutputPixels(U8 *p, S32 nPostLinePixelOffset, bool bRGBA) const {
	Rect<S32> rect;
	GetBoundingRect(&rect);
	S32 nBottom = rect.Bottom(), nRight = rect.Right();
	if (bRGBA) {
		for(S32 nRow = rect.Top(); nRow <= nBottom; ++nRow) {
			for(S32 nCol = rect.Left(); nCol <= nRight; ++nCol) {
				*p++ = GetPix(nRow, nCol);
				*p++ = GetPix(nRow, nCol);
				*p++ = GetPix(nRow, nCol);
				*p++ = 0xFF;
			}
			p += nPostLinePixelOffset * 4;
		}
	} else {
		for(S32 nRow = rect.Top(); nRow <= nBottom; ++nRow) {
			for(S32 nCol = rect.Left(); nCol <= nRight; ++nCol) 
				*p++ = GetPix(nRow, nCol);
			p += nPostLinePixelOffset;
		}
	}
}

void View::TransformPoints(F64 *nRow, F64 *nCol) const
{
	F64 nRow2 = *nRow, nCol2 = *nCol;
		
	nRow2 = -nRow2; /* conversion to make trigo easier */
	
	F64 nRow3, nCol3;
	nRow3 = nCol2 * Math::Sin(m_nCenterAngle) + nRow2 * Math::Cos(m_nCenterAngle);
	nCol3 = nCol2 * Math::Cos(m_nCenterAngle) - nRow2 * Math::Sin(m_nCenterAngle);
	
	nRow3 = -nRow3; /* undo conversion from before */
	
	*nRow = nRow3 + m_nCenterRow;
	*nCol = nCol3 + m_nCenterCol;	
}

void View::UntransformPoints(F64 *nRow, F64 *nCol) const
{
	
	/* first, we move the points */
	*nRow -= m_nCenterRow;
	*nCol -= m_nCenterCol;
	
	/* second, we rotate in the opposite direction */
	View v = View::View(NULL, 0, 0, -m_nCenterAngle);
	v.TransformPoints(nRow, nCol);
	
}

void View::GetBoundingRect(Rect <S32> *pRect) const
{
	F64 nRow1 = 0, nCol1 = 0;
	F64 nRow2 = 0, nCol2 = m_pImage->GetWidth() - 1;
	F64 nRow3 = m_pImage->GetHeight() - 1, nCol3 = 0;
	F64 nRow4 = m_pImage->GetHeight() - 1, nCol4 = m_pImage->GetHeight() - 1;
	
	UntransformPoints(&nRow1, &nCol1);
	UntransformPoints(&nRow2, &nCol2);
	UntransformPoints(&nRow3, &nCol3);
	UntransformPoints(&nRow4, &nCol4);

	pRect->SetLeft((S32)floor(nCol1));
	pRect->SetRight((S32)ceil(nCol1));
	pRect->SetTop((S32)floor(nRow1));
	pRect->SetBottom((S32)ceil(nRow1));
	
	if (nCol2 < pRect->Left()) pRect->SetLeft((S32)floor(nCol2)); 
	if (nCol3 < pRect->Left()) pRect->SetLeft((S32)floor(nCol3)); 
	if (nCol4 < pRect->Left()) pRect->SetLeft((S32)floor(nCol4)); 
	
	if (nCol2 > pRect->Right()) pRect->SetRight((S32)ceil(nCol2)); 
	if (nCol3 > pRect->Right()) pRect->SetRight((S32)ceil(nCol3)); 
	if (nCol4 > pRect->Right()) pRect->SetRight((S32)ceil(nCol4)); 

	if (nRow2 < pRect->Top()) pRect->SetTop((S32)floor(nRow2)); 
	if (nRow3 < pRect->Top()) pRect->SetTop((S32)floor(nRow3)); 
	if (nRow4 < pRect->Top()) pRect->SetTop((S32)floor(nRow4)); 
	
	if (nRow2 > pRect->Bottom()) pRect->SetBottom((S32)ceil(nRow2)); 
	if (nRow3 > pRect->Bottom()) pRect->SetBottom((S32)ceil(nRow3)); 
	if (nRow4 > pRect->Bottom()) pRect->SetBottom((S32)ceil(nRow4)); 
	
}
